﻿using System;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml.Linq;
using CodeCrawler.UI.Data;
using CodeCrawler.UI.Engine.KeyPointersScan;
using OWASP.CodeReview.CodeCrawler.Engine;
using OWASP.CodeReview.CodeCrawler.Engine.Exceptions;
using OWASP.CodeReview.CodeCrawler.Enums;
using Puzzle.SourceCode;
using Puzzle.Windows.Forms.SyntaxBox;

namespace CodeCrawler.UI.Forms.ChildForms {
    public partial class SingleFileScan : Form {
        /// <summary>
        /// This will act as a container for all the threats found into the current file
        /// </summary>
        public DataTable CurrentThreatListDataTable = new DataTable("ThreatList");


        /// <summary>
        /// Constructor
        /// </summary>
        public SingleFileScan() {
            InitializeComponent();
            /// Initialise CodeCrawler Database
            var databaseHelper = new CodeCrawlerXMLDatabase();
            LoadCodeReviewDataBase(databaseHelper);
            SetCodeViewerValues();
            
        }


        public SingleFileScan(string fileName) {
            InitializeComponent();
            /// Initialise CodeCrawler Database
            var databaseHelper = new CodeCrawlerXMLDatabase();

            FileName = fileName;
            LoadCodeReviewDataBase(databaseHelper);
            SetCodeViewerValues();
            IsAlreadyArchivied = false;
            Text = FileName;
        }


        /// <summary>
        /// Properties
        /// </summary>
        public string FileName { get; set; }
        public bool IsSourceCodeFileAlreadyLoaded { get; set; }
        public string description { get; set; }
        public bool IsAlreadyArchivied { get; set; }

        public ThreatLevel LEVEL { get; set; }
        public DataSet xmlCodeCrawlerDatabase { get; set; } // CodeCrawler Database
        public DataTable threatList { get; set; } // Code Crawler ThreatList
        internal string htmlReportPath { get; set; }
        internal string rtfReportPath { get; set; }
        internal int totalRow { get; set; }


        /// <summary>
        /// SETUP Procedure
        /// </summary>
        internal void CleanUpProcedure() {
            SingleFileCodeViewer.Text = string.Empty;
            CodeViewer.Visible = true;
            CodeViewer.Text = string.Empty;
            IsSourceCodeFileAlreadyLoaded = true;
            ReadSourceFile();
            gocode_Click();
        }


        private void gocode_Click() {
            /// AlterSourceCodeViewer       -> Different Thread
            var sourceCodeViewerColorsthreadStart
                = new ThreadStart(AlterSourceCodeViewerColor);

            // Separate CodeColouring from the UI Thread
            var codeColouring = new Thread(sourceCodeViewerColorsthreadStart);
            codeColouring.Start();
            // Show total number of Threats

            int totalNumberThreats = CurrentThreatListDataTable.Rows.Count;

            if (totalNumberThreats < 10) {
                ui_TKLoCNumbers.ForeColor = Color.Green;
            }
            else if (totalNumberThreats > 20) {
                ui_TKLoCNumbers.ForeColor = Color.Orange;
            }
            else if (totalNumberThreats > 30) {
                ui_TKLoCNumbers.ForeColor = Color.Red;
            }


            toolStripLabel11.Text = CurrentThreatListDataTable.Rows.Count.ToString();
            
            // Percentage of Errors
            int totalNumberOfLinesOfCode = SingleFileCodeViewer.Document.Lines.Count();
            toolStripProgressBar1.Maximum = totalNumberOfLinesOfCode;

            for (int i = 0; i <= totalNumberThreats; i++)
            {
                toolStripProgressBar1.PerformStep();    
            }

            var kLocValue = (totalNumberOfLinesOfCode/totalNumberThreats);
            ui_TKLoCNumbers.Text = string.Concat(kLocValue.ToString(), "/100");


        }


        /// <summary>
        /// This piece of code should be moved into OWASP.CodeReview.CodeCrawler.Engine namespace
        /// </summary>
        internal void DoSingleFileScan(object sender, EventArgs e) {
            ReadSourceFile();
            ScanEngine(ref CurrentThreatListDataTable, xmlCodeCrawlerDatabase, FileName);
            gocode_Click();
        }


        public static void ScanEngine(ref DataTable currentThreatListDataTable,
                                      DataSet xmlCodeCrawlerDatabase,
                                      string fileName) {
            if (File.Exists(fileName)) {
                TextReader re = File.OpenText(fileName);
                string input = null;
                int lineNumber = 1;

                var threatCollectionList = new ThreatCollection();
                var databaseObjects = new CodeCrawlerXMLDatabase();

                while ((input = re.ReadLine()) != null) {
                    Threat threatObject = threatCollectionList.ParseLineOfCode(input, xmlCodeCrawlerDatabase, lineNumber);

                    if (threatObject != null) {
                        var collection = new ThreatCollection();
                        collection.AddThreatItem(threatObject, currentThreatListDataTable);
                    }

                    lineNumber++;
                }

                re.Close();
            }
        }


        /// <summary>
        /// Alternate Code Colouring
        /// </summary>
        private void AlterSourceCodeViewerColor() {
            int lineNumber;

            try {
                for (int index = 0;
                     index < CurrentThreatListDataTable.Rows.Count;
                     index++) {
                    lineNumber = Convert.ToInt32(CurrentThreatListDataTable.Rows[index][3]);
                    Row r;
                    r = SingleFileCodeViewer.Document[lineNumber - 1];

                    switch ((ThreatLevel) CurrentThreatListDataTable.Rows[index][2]) {
                        case ThreatLevel.Green:
                            r.BackColor = Color.DarkSeaGreen;
                            r.Tag = index;
                            break;
                        case ThreatLevel.Yellow:
                            r.BackColor = Color.Orange;
                            r.Tag = index;
                            break;
                        case ThreatLevel.Red:
                            r.BackColor = Color.Red;
                            r.Tag = index;
                            break;
                        default:
                            r.BackColor = Color.Red;
                            r.Tag = index;
                            break;
                    }
                }
            }
            catch (InvalidCastException) {
                //TODO: This error should happen only when some one has changed the datatable
                //should log a message into a logger
            }
        }


        /// <summary>
        /// Read Source File
        /// </summary>
        private void ReadSourceFile() {
            var builder = new StringBuilder();
            using (TextReader reader = File.OpenText(FileName)) {
                string line;
                while ((line = reader.ReadLine()) != null) {
                    builder.AppendLine(line);
                }
            }

            SingleFileCodeViewer.Document.Text = builder.ToString();
        }


        /// <summary>
        /// Load Code Crawler Database
        /// </summary>
        /// <param name="DatabaseHelper"></param>
        private void LoadCodeReviewDataBase(CodeCrawlerXMLDatabase DatabaseHelper) {
            DatabaseHelper.CreateThreatTable(CurrentThreatListDataTable);
            xmlCodeCrawlerDatabase = DatabaseHelper.InizializeCodeCrawlerDatabase();
        }


        /// <summary>
        /// Prepare Editor
        /// </summary>
        private void SetCodeViewerValues() {
            //SingleFileCodeViewer.Document.SyntaxFile = Application.StartupPath + "\\C#.syn";
            SingleFileCodeViewer.Document.SyntaxFile = Application.StartupPath + "\\DarkMoon.syn";
            CodeViewer.Document.Text = string.Empty;
        }


        private void RowClickEvent(object sender, RowMouseEventArgs e) {
            if (e.Row.Tag != null) {
                int tableIndex = Convert.ToInt32(e.Row.Tag);

                totalRow = Convert.ToInt32(e.Row.Index + 1);

                // Search within ThreatTable where Column 3 is equal to tableRow
                string description;

                try {
                    description = (from myRow in CurrentThreatListDataTable.AsEnumerable()
                                   where myRow.Field<int>("Line") == totalRow
                                   select myRow.Field<string>("Description")).Single();
                }
                catch (InvalidOperationException) // This may happens when no threat are onto the list
                {
                    return;
                }


                string threatName = (from myRow in CurrentThreatListDataTable.AsEnumerable()
                                     where myRow.Field<int>("Line") == totalRow
                                     select myRow.Field<string>("Threat")).Single();

                // Set like Description and FileName depeding on Findings
                textBox3.Text = description;
                SourceCodeFoundInTextBox.Text = FileName;


                // THIS IS HOW WE POPULATE STRIDE LABELS WITHIN THE SINGLE FILE SCAN WINDOW
                // NOTE: This is a problem that i might fix later. I know it is not the best 
                //       performance prof code but it still does the job.
                var xmlDatabasePath = new StringBuilder();
                xmlDatabasePath.Append(Application.StartupPath);
                xmlDatabasePath.Append("/DataBase");
                xmlDatabasePath.Append("/CodeCrawlerDatabase.xml");

                // Loading from a file, you can also load from a stream
                XDocument loaded = XDocument.Load(xmlDatabasePath.ToString());

                string strideAutomaticCategory = string.Empty;


                try {
                    strideAutomaticCategory = (loaded.Descendants("KeyPointer").Where(
                        keypointers => (string) keypointers.Element("k_name") == threatName).Select(
                        keypointers =>
                        (string) keypointers.Element("Stride").Element("Item").Element("Category") + ";" +
                        (string) keypointers.Element("Stride").Element("Item").Element("Control"))).Single();
                }
                catch (NullReferenceException) {
                    {
                        ui_StrideControl.Text = string.Empty;
                        ui_strideCategory.Text = string.Empty;
                    }
                }

                if (!string.IsNullOrEmpty(strideAutomaticCategory)) {
                    string[] strideSplitCategory = strideAutomaticCategory.Split(';');
                    ui_StrideControl.Text = strideSplitCategory[1].ToString();
                    ui_strideCategory.Text = strideSplitCategory[0].ToString();
                }

                ui_KeywordDescription.Text = threatName;
            }
        }


        /// <summary>
        /// Archive file
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripButton1_Click(object sender, EventArgs e) {
            if (!IsAlreadyArchivied) {
                GenerateDump(CurrentThreatListDataTable, FileName);
                MessageBox.Show("File Archived", "File successfully archived", MessageBoxButtons.OK,
                                MessageBoxIcon.Information);
                IsAlreadyArchivied = true;
            }
        }


        /// <summary>
        /// Create a Dump of the scanned file
        /// and store it into /ApplicationPath/Results/DumpData
        /// </summary>
        /// <param name="currentFileDump"></param>
        /// <param name="FileName"></param>
        private static string GenerateDump(DataTable currentFileDump, string FileName) {
            string generatedDumpFolderPath = string.Concat(Application.StartupPath, "/Results/DumpsData/");

            // Check if folder is exists
            var generatedDumpFolderInfo = new DirectoryInfo(generatedDumpFolderPath);
            if (!generatedDumpFolderInfo.Exists) {
                // Folder doesn't exists, create.
                generatedDumpFolderInfo.Create();
            }

            // Assume creating dump folder won't be a problem, the folder IO has been checked.
            var currentFileInfo = new FileInfo(FileName);

            if (currentFileInfo.Exists) {
                // This may look like huge, but in order to avoid duplicates 
                // additional informations need to be attached to the file name
                // DumpFile Structure
                // - currentFileName (MySourceCodeFile [without.cs])
                //                  - Month
                //                  - Day
                //                  - Year
                //                  - Hours
                //                  - Minutes
                //                           - .xml

                string dumpfileLocation = string.Concat(generatedDumpFolderPath, currentFileInfo.Name,
                                                        DateTime.Now.Month.ToString(), DateTime.Now.Day.ToString(),
                                                        DateTime.Now.Year.ToString(), DateTime.Now.Hour.ToString(),
                                                        DateTime.Now.Minute.ToString(), ".owasp");

                currentFileDump.WriteXml(dumpfileLocation);
                return dumpfileLocation;
            }
            else {
                // Unable to retrieve source code file name. File doesn't exists
                throw new SourceCodeFileDoesntExistAnyMoreException();
            }
        }


        /// <summary>
        /// Print
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripButton2_Click(object sender, EventArgs e) {
            SourceCodePrintDocument pd;
            pd = new SourceCodePrintDocument(SingleFileCodeViewer.Document);
            printDialog1.Document = pd;
            if (printDialog1.ShowDialog(this) == DialogResult.OK)
                pd.Print();
        }


        /// <summary>
        /// Start Notepad
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripLabel3_Click(object sender, EventArgs e) {
            Process.Start("Notepad.exe");
        }


        /// <summary>
        /// Start calc
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripLabel4_Click(object sender, EventArgs e) {
            Process.Start("calc");
        }


        /// <summary>
        /// Start Google
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripLabel6_Click(object sender, EventArgs e) {
            Process.Start("http://www.google.com");
        }


        /// <summary>
        /// Start MSDN
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripLabel7_Click(object sender, EventArgs e) {
            Process.Start("http://www.msdn.com");
        }


        private void toolStripButton3_Click(object sender, EventArgs e) {
            string path = GenerateDump(CurrentThreatListDataTable, FileName);

            var report = new GenerateReport();
            report.dumpLocation = path;
            report.MdiParent = MdiParent;
            report.Location = new Point(60, 60);
            report.Show();
        }


        /// <summary>
        /// Remove False positive from Threat Data List
        /// </summary>
        private void removeThreatFromThreatDataListTable() {
            foreach (DataRow dataRow in CurrentThreatListDataTable.Rows) {
                if (int.Parse(dataRow[3].ToString()) == totalRow) {
                    DataRow toBeRemoved = dataRow;
                    CurrentThreatListDataTable.Rows.Remove(toBeRemoved);
                    break;
                }
            }
        }


        /// <summary>
        /// Clean up Threat Analysis Box
        /// </summary>
        private void cleanUpThreatAnalysisBox() {
            ui_StrideControl.Text = string.Empty;
            ui_strideCategory.Text = string.Empty;
            ui_KeywordDescription.Text = string.Empty;
            textBox3.Text = string.Empty;
        }


        /// <summary>
        /// Redo Threat Count Colouring depending on actual data
        /// </summary>
        private void redoCodeColouring() {
            int totalNumberThreats = CurrentThreatListDataTable.Rows.Count;

            if (totalNumberThreats < 10) {
                ui_TKLoCNumbers.ForeColor = Color.Green;
            }
            else if (totalNumberThreats > 20) {
                ui_TKLoCNumbers.ForeColor = Color.Orange;
            }
            else if (totalNumberThreats > 30) {
                ui_TKLoCNumbers.ForeColor = Color.Red;
            }

            /// Update Threat Counts
            ui_TKLoCNumbers.Text = CurrentThreatListDataTable.Rows.Count.ToString();
        }

        private void toolStripButton4_Click(object sender, EventArgs e)
        {
                        if (totalRow >= 1) {
                // Remove Highlighting changing the background colour to White
                Row r;
                r = SingleFileCodeViewer.Document[totalRow - 1];
                r.BackColor = Color.White;

                //Clean Up Threat Analysis Box
                cleanUpThreatAnalysisBox();

                //Remove from Threat Table
                removeThreatFromThreatDataListTable();
            }

            // Refresh Editor
            SingleFileCodeViewer.Refresh();

            // Do Threat Colouring
            redoCodeColouring();

            /// Disable Button
            if (CurrentThreatListDataTable.Rows.Count == 0) {
                toolStripButton4.Enabled = false;
            }
        }

        private void toolStripButton1_Click_1(object sender, EventArgs e)
        {
            AddThreat addThreatForm = new AddThreat();
            addThreatForm.MdiParent = this.MdiParent;
            addThreatForm.Show();
        }
    }
}